(function () {
	'user strict';

	var libRoot = (function () {
		var href = $('<a>').attr('href', '#').prop('href');
		var i = href.indexOf('/html/');
		return i > 0 ? href.substring(0, i) + '/lib/' : '/lib/';
	})();

	var loadBootstrapDepends = makeDependsLoader([
        libRoot + "bootstrap-3.3.5/js/bootstrap.js"
    ], [
			libRoot + "bootstrap-3.3.5/css/bootstrap.min.css"
		]);
	var loadResizeDepends = makeDependsLoader([
        libRoot + "jquery.resize-0.5.3/jquery.resize.js"
    ]);
	var loadJquiDepends = makeDependsLoader([
        libRoot + "jquery-ui-1.11.4/jquery-ui.min.js"
    ]);
	var loadLayoutDepends = makeDependsLoader([
        libRoot + "jquery.layout-1.4.0/jquery.layout.js"
    ], [
			libRoot + "jquery.layout-1.4.0/jquery.layout-default.css"
		]);
	var loadSpinDepends = makeDependsLoader([
        libRoot + "spin.js-2.3.2/spin.min.js"
    ]);
	var loadFueluxDepends = makeDependsLoader([
        libRoot + "fuelux-3.15.3/js/fuelux.js"
    ], [
			libRoot + "fuelux-3.15.3/css/fuelux.css"
		]);
	var loadFileUploadDepends = makeDependsLoader([
		libRoot + "jQuery-File-Upload-9.12.5/js/jquery.fileupload.js"
	], [
			libRoot + "jQuery-File-Upload-9.12.5/css/jquery.fileupload.css"
		]);
	var loadValidationDepends = makeDependsLoader([
        libRoot + "jquery-validation-1.15.0/jquery.validate.js",
        libRoot + "jquery-validation-1.15.0/localization/message_zh.js"
    ]);
	var loadDatetimePickerDepends = makeDependsLoader([
        libRoot + "bootstrap-datetimepicker-2.3.8/js/bootstrap-datetimepicker.js",
        libRoot + "bootstrap-datetimepicker-2.3.8/js/locales/bootstrap-datetimepicker.zh-CN.js"
    ], [
			libRoot + "bootstrap-datetimepicker-2.3.8/css/bootstrap-datetimepicker.css"
		]);
	var loadEchartsDepends = makeDependsLoader([
        libRoot + "echarts-2.2.7/echarts-all.js",
        libRoot + "echarts-2.2.7/theme/default.js",
        libRoot + "echarts-2.2.7/theme/blue.js",
        libRoot + "echarts-2.2.7/theme/dark.js",
        libRoot + "echarts-2.2.7/theme/infographic.js"
    ]);
	var loadDataTablesDepends = makeDependsLoader([
        libRoot + "datatables-1.10.11/js/jquery.dataTables.js",
        libRoot + "datatables-1.10.11/js/dataTables.bootstrap.min.js",
        libRoot + "datatables-1.10.11/ext/Scroller/js/dataTables.scroller.min.js",
        libRoot + "datatables-1.10.11/ext/Select/js/dataTables.select.min.js",
        libRoot + "datatables-1.10.11/ext/Buttons/js/dataTables.buttons.js",
        libRoot + "datatables-1.10.11/ext/Buttons/js/buttons.bootstrap.js",
        libRoot + "datatables-1.10.11/ext/Buttons/js/buttons.colVis.js",
        libRoot + "datatables-1.10.11/ext/Buttons/js/buttons.flash.js",
        libRoot + "jszip-2.6.0/jszip.js",
        libRoot + "datatables-1.10.11/ext/Buttons/js/buttons.html5.js",
        libRoot + "datatables-1.10.11/ext/Buttons/js/buttons.print.js"
    ], [
			libRoot + "datatables-1.10.11/css/dataTables.bootstrap.min.css",
			libRoot + "datatables-1.10.11/ext/Select/css/select.bootstrap.min.css",
			libRoot + "datatables-1.10.11/ext/Buttons/css/buttons.bootstrap.min.css"
		]);
	var loadUeditorDepends = makeDependsLoader([
        libRoot + "ueditor-1.4.3.3/ueditor.config.js",
        libRoot + "ueditor-1.4.3.3/ueditor.all.js"
		//libRoot + "ueditor-1.4.3.3/lang/zh-cn/zh-cn.js"
    ]);

	var uirouter;
	try {
		uirouter = angular.module('ui.router');
	} catch (e) { }
	var ui2ng = angular.module('mtUtils', uirouter ? ['ui.router'] : []);

	ui2ng.run(function ($q) {
		loadBootstrapDepends($q);
	});

	uirouter && ui2ng.config(function ($provide, $stateProvider, $urlRouterProvider) {
		$provide.decorator("uiViewDirective", function ($delegate) {
			$.each($delegate, function (i, delegate) {
				delegate.compile = buildCompile(delegate.compile);
			});
			return $delegate;
		});

		function buildCompile(compile) {
			return function (element, attrs) {
				if (!element.hasClass('tab-content'))
					return compile.apply(this, arguments);

				var parents = [];
				element.parents('.tab-pane').each(function (i, el) {
					if ($(el).parent('.tab-content[ui-view],ui-view.tab-content').length)
						parents.unshift(el.id);
				});
				var parent = parents.join('.');

				element.children('.tab-pane.active').each(function (i, el) {
					$urlRouterProvider.otherwise('/' + el.id);
				});

				element.children('.tab-pane').addClass('active in').each(function (i, el) {
					var name = el.id;
					var url = '^/' + name;
					var tmpl = el.outerHTML;
					$stateProvider.state(name, {
						parent: parent,
						url: url,
						template: tmpl
					});
				});

				element.empty();

				return compile.apply(this, arguments);
			};
		}
	});

	var ueditorIdCounter = 0;
	ui2ng.directive("mtUeditor", function ($q) {
		window.UEDITOR_HOME_URL = libRoot + 'ueditor-1.4.3.3/';

		return {
			restrict: 'A',
			require: '?ngModel',
			link: link
		};

		function link(scope, element, attrs, ngModel) {
			loadUeditorDepends($q).then(function () {
				var option = scope.$eval(attrs.mtUeditor);
				var option = $.extend({}, option, {
					UEDITOR_HOME_URL: libRoot + 'ueditor-1.4.3.3/',
					serverUrl: libRoot + '../ueditor/jsp/controller.jsp'
				});

				var ueditorId = 'ueditor' + ++ueditorIdCounter;
				$('<script type="text/plain">').attr('id', ueditorId).appendTo(element);

				var ue = UE.getEditor(ueditorId, option);
				if (!ngModel)
					return;

				ue.addListener('ready', function () {
					ngModel.$render = function () {
						ue.setContent(ngModel.$viewValue || '');
					};

					ue.setContent(ngModel.$viewValue || '');
				});

				ue.addListener('contentChange', function () {
					if (scope.$$phase) {
						ngModel.$setViewValue(ue.getContent());
					} else {
						scope.$apply(function () {
							ngModel.$setViewValue(ue.getContent());
						});
					}
				});
			});
		}
	});

	ui2ng.directive("mtDatetimePicker", function ($q) {
		return {
			restrict: 'A',
			link: link
		};

		function link(scope, element, attrs) {
			$q.all([loadBootstrapDepends($q), loadDatetimePickerDepends($q)]).then(function () {
				var option = scope.$eval(attrs.mtDatetimePicker);
				element.datetimepicker(option);
			});
		}
	});

	ui2ng.directive("mtFileUpload", function ($q) {
		return {
			restrict: 'A',
			link: link,
			scope: {},
			template: '<span>{{text[status]}}</span><input type="file" ng-disabled="status==\'done\'"><input type="hidden" name="{{name}}">' +
			'<div class="progress hidden" ng-class="{hidden: status!=\'doing\'}"><div class="progress-bar progress-bar-{{barStyle}}"></div></div>'
		};

		function link(scope, element, attrs) {
			scope.text = {
				initial: '上传文件...',
				doing: '正在上传...',
				done: '文件已上传.',
				error: '文件上传失败.'
			};

			$q.all([loadBootstrapDepends($q), loadJquiDepends($q), loadFileUploadDepends($q)])
				.then(function () {
					var option = scope.$eval(attrs.mtFileUpload);

					scope.status = 'initial';
					scope.name = option && option.paramName;
					scope.barStyle = 'success';

					var uploader = element.fileupload(option);

					uploader.on('fileuploadadd', function (e, data) {
						element.find('.progress.hidden').removeClass('hidden');
					});

					uploader.on('fileuploadprogress', function (e, data) {
						var progress = parseInt(data.loaded / data.total * 100, 10);
						element.find('.progress .progress-bar').css(
							'width',
							progress + '%'
						);
					});

					uploader.on('fileuploaddone', function (e, data) {
						scope.text = text.done;
					});
				});
		}
	});

	ui2ng.directive("mtValidate", function ($q) {
		return {
			restrict: 'A',
			link: link
		};

		function link(scope, element, attrs) {
			loadValidationDepends($q).then(function () {
				var option = scope.$eval(attrs.mtValidate);
				var validator = element.validate(option);

				scope.$on('reset.validator', function (event, target) {
					if (!target || element.is(target))
						validator.resetForm();
				});
			});
		}
	});

	ui2ng.directive("mtLayout", function ($http, $compile, $log, $q) {
		return {
			restrict: 'A',
			link: link
		};

		function setup(scope, element, attrs) {
			$q.all([loadJquiDepends($q), loadLayoutDepends($q), loadResizeDepends($q)])
				.then(function () {
					var option = attrs.mtLayout ? scope.$eval(attrs.mtLayout) : {};
					var mtLayout = element.layout(option);

					element.resize(function () {
						if (element.width() > 0 && element.height() > 0) {
							mtLayout.resizeAll();
						}
					});
					element.children('.ui-layout-pane')
						.on('show.bs.dropdown', function () {
							mtLayout.allowOverflow(this);
						})
						.on('hidden.bs.dropdown', function () {
							mtLayout.resetOverflow(this);
						});
				});
		}

		function link(scope, element, attrs) {
			if (attrs.layoutUrl) {
				$http.get(attrs.layoutUrl).then(function (response) {
					element.html(response.data);
					$compile(element.contents())(scope);
					setup(scope, element, attrs);
				});
			} else {
				setup(scope, element, attrs);
			}
		}
	});

	ui2ng.directive("mtUrl", function ($http, $compile, $log) {
		return {
			restrict: 'A',
			link: link
		};

		function link(scope, element, attrs) {
			var eh = makeEventHandler(scope, element, attrs, "load.url");
			scope.$watch(
				function (scope) {
					return attrs.mtUrl;
				},
				function (value) {
					$http.get(value).then(function (response) {
						element.html(response.data);
						$compile(element.contents())(scope);
						eh(value);
					});
				}
			);
		}
	});

	ui2ng.directive("mtTab", function ($q) {
		return {
			restrict: 'A',
			link: link
		};

		function link(scope, element, attrs) {
			loadBootstrapDepends($q).then(function () {
				element.on("show.bs.tab", makeTabEventHandler(scope, element, attrs, "show.bs.tab"));
				element.on("shown.bs.tab", makeTabEventHandler(scope, element, attrs, "shown.bs.tab"));
				element.on("hide.bs.tab", makeTabEventHandler(scope, element, attrs, "hide.bs.tab"));
				element.on("hidden.bs.tab", makeTabEventHandler(scope, element, attrs, "hidden.bs.tab"));

				scope.$on('show.tab', function (event, target) {
					var s = element.attr('href') || element.attr('data-target');

					if ($(s).is(target)) {
						element.parent('li').removeClass('active');
						element.tab('show');
					}
				});
			});
		}

		function makeTabEventHandler(scope, element, attrs, eventType) {
			return function (event) {
				var targetSelector = element.attr('data-target') || element.attr('href');
				var target = $(targetSelector);

				if (scope.$$phase) {
					scope.$root.$broadcast(eventType, element, target, event);
				} else {
					scope.$apply(function () {
						scope.$root.$broadcast(eventType, element, target, event);
					});
				}
			};
		}
	});

	ui2ng.directive("mtModal", function ($q) {
		return {
			restrict: 'A',
			link: link
		};

		function link(scope, element, attrs) {
			loadBootstrapDepends($q).then(function () {
				element.on("show.bs.modal", makeEventHandler(scope, element, attrs, "show.bs.modal"));
				element.on("shown.bs.modal", makeEventHandler(scope, element, attrs, "shown.bs.modal"));
				element.on("hide.bs.modal", makeEventHandler(scope, element, attrs, "hide.bs.modal"));
				element.on("hidden.bs.modal", makeEventHandler(scope, element, attrs, "hidden.bs.modal"));

				scope.$on('show.modal', function (event, target) {
					if (element.is(target)) {
						element.modal('show');
					}
				});
			});
		}
	});

	ui2ng.directive("mtTree", function ($http, $compile, $log, $q) {
		return {
			restrict: 'A',
			link: link
		};

		function link(scope, element, attrs) {
			$q.all([loadSpinDepends($q), loadFueluxDepends($q)])
				.then(function () {
					var defaultOption = {
						dataSource: sampleDataSource,
						multiSelect: false,
						folderSelect: true
					};
					var option = scope.$eval(attrs.mtTree) || defaultOption;
					var mtTree = element.tree(option);

					mtTree.on('selected.fu.tree', makeEventHandler(scope, element, attrs, 'selected.fu.tree'));
					mtTree.on('deselected.fu.tree', makeEventHandler(scope, element, attrs, 'deselected.fu.tree'));

					scope.$on('openFolder.tree', function (event, target) {
						if (!target || element.is(target)) {
							var selectedItem = mtTree.find('.tree-branch.tree-selected');
							mtTree.tree('openFolder', selectedItem);
						}
					});

					scope.$on('updateItem.tree', function (event, target, data) {
						if (!target || element.is(target)) {
							var selectedItem = mtTree.find('.tree-branch.tree-selected');
							selectedItem.data(data);
							selectedItem.children(".tree-branch-header,.tree-item-name")
								.find('.tree-label').text(data.name);
						}
					});

					scope.$on('refresh.tree', function (event, target) {
						if (!target || element.is(target)) {
							element.find(".tree-branch, .tree-item").not(".hide").remove();
							mtTree.tree('render');
						}
					});

					scope.$on('refreshFolder.tree', function (event, target) {
						if (!target || element.is(target)) {
							var selectedItem = mtTree.find('.tree-branch.tree-selected');
							mtTree.tree('refreshFolder', selectedItem);
						}
					});

					scope.$on('refreshParentFolder.tree', function (event, target) {
						if (!target || element.is(target)) {
							var selectedItem = mtTree.find('.tree-branch.tree-selected').parent().closest('.tree-branch');
							mtTree.tree('refreshFolder', selectedItem);
						}
					});
				});
		}

		function sampleDataSource(openedParentData, callback) {
			var childNodesArray = [
				{
					"name": "Ascending and Descending",
					"type": "folder"
                },
				{
					"name": "Sky and Water I",
					"type": "item"
                },
				{
					"name": "Drawing Hands",
					"type": "folder"
                },
				{
					"name": "waterfall",
					"type": "item"
                },
				{
					"name": "Belvedere",
					"type": "folder"
                },
				{
					"name": "Relativity",
					"type": "item"
                },
				{
					"name": "House of Stairs",
					"type": "folder"
                },
				{
					"name": "Convex and Concave",
					"type": "item"
                }
            ];

			callback({
				data: childNodesArray
			});
		}
	});

	ui2ng.directive("mtTable", function ($http, $compile, $log, $q) {
		return {
			restrict: 'A',
			link: link
		};

		function link(scope, element, attrs) {
			$q.all([loadBootstrapDepends($q), loadDataTablesDepends($q)])
				.then(function () {
					var parent = element.parent();
					var option = scope.$eval(attrs.mtTable);
					var mtTable = element.DataTable(option);

					var events = ["init.dt", "draw.dt", "order.dt", "page.dt", "search.dt", "select", "deselect"];
					for (var i = 0; i < events.length; ++i)
						mtTable.on(events[i], makeEventHandler(scope, element, attrs, events[i]));

					mtTable.on("init.dt", function () {
						parent.resize(function () {
							if (parent.width() > 0 && parent.height() > 0) {
								mtTable.columns.adjust();
								option && option.scroller && mtTable.scroller.measure();
							}
						});
					});
					mtTable.on('column-sizing.dt', function (e, settings) {
						if (option != null && option.scrollY == '100%' && parent.height() > 0) {
							var container = $(mtTable.table().container());
							var extra = container.height() -
								container.find(".dataTables_scrollBody").height();
							var oldHeight = container.find(".dataTables_scrollBody").height();
							var newHeight = parent.height() - extra;
							if (oldHeight - newHeight > 16 || newHeight - oldHeight > 16) {
								container.find(".dataTables_scrollBody").height(newHeight);
								option && option.scroller && mtTable.scroller.measure();
							}
						}
					});
					scope.$on('resize.table', function (event, target, paging) {
						if (!target || element.is(target)) {
							mtTable.columns.adjust();
							option && option.scroller && mtTable.scroller.measure();
						}
					});
					scope.$on('refresh.table', function (event, target, paging) {
						if (!target || element.is(target))
							mtTable.draw();
					});
				});
		}
	});

	ui2ng.directive("mtEcharts", function ($rootScope, $log, $q) {
		return {
			restrict: 'A',
			link: link
		};

		function setup(scope, element, attrs, theme, option) {
			var mtChart = echarts.init(element[0], theme || 'default');
			option && mtChart.setOption(option);

			angular.forEach(echarts.config.EVENT, function (value, key) {
				mtChart.on(value, makeEventHandler(scope, element, attrs, value + '.echarts'));
			});

			return mtChart;
		}

		function link(scope, element, attrs) {
			$q.all([loadEchartsDepends($q), loadResizeDepends($q)]).then(function () {
				var mtChart, option, theme, ratio, tmpl;

				theme = scope.$eval(attrs.echartsTheme);
				ratio = +scope.$eval(attrs.echartsRatio);
				tmpl = scope.$eval(attrs.echartsSeries);

				element.resize(initialResizeHandler);

				function initialResizeHandler() {
					if (element.width() > 0 && ratio > 0) {
						var nh = Math.round(element.width() / ratio);
						(nh != element.height()) && element.height(nh);
					}

					if (element.width() > 0 && element.height() > 0) {
						element.removeResize(initialResizeHandler);
						mtChart = setup(scope, element, attrs, theme, option);
						element.resize(finalResizeHandler);
					}
				}

				function finalResizeHandler() {
					if (element.width() > 0 && ratio > 0) {
						var nh = Math.round(element.width() / ratio);
						(nh != element.height()) && element.height(nh);
					}

					mtChart.resize();
				}

				scope.$watch(function () {
					option = scope.$eval(attrs.mtEcharts);
					return option;
				}, function (newVal, oldVal) {
					if (!option || !option.series || !(option.series instanceof Array)) {
						$log.warn('mt-charts:', 'invalid option -', option);
						return;
					}
					mtChart && mtChart.setOption(option, true);
				});

				scope.$watch(function () {
					data = scope.$eval(attrs.echartsData);
					return data;
				}, function (newVal, oldVal) {
					var data = newVal;
					if (!option || !data)
						return;

					if (!(data.cols instanceof Array) || !(data.rows instanceof Array) || !(data.data instanceof Array)) {
						$log.warn('mt-echarts:', 'invalid data -', data);
						return;
					}

					for (var i = 0; i < data.data.length && i < data.rows.length; ++i) {
						if (i >= option.series.length) {
							option.series[i] = $.extend(true, {}, tmpl || option.series[option.series.length - 1]);
						}

						switch (option.series[i].type) {
							case 'line':
							case 'bar':
								processEchartsLineBarOption(option, i, data);
								break;
							case 'pie':
							case 'map':
							case 'gauge':
								processEchartsPieMapGaugeOption(option, i, data);
								break;
							case 'radar':
								processEchartsRadarOption(option, i, data);
								break;
						}
					}

					if (i < option.series.length)
						option.series.splice(i, option.series.length - i);

					mtChart && mtChart.setOption(option, true);
				});

			});
		}
	});

	// 存取后台数据
	// 参数说明:
	// url          - 后台服务地址，字符串      ，必需
	// params       - 参数对象    ，JS对象      ，可选
	// method       - HTTP方法    ，字符串      ，可选 ('GET', 'POST', 'PUT', 'DELETE')
	// config       - 配置对象    ，JS对象      ，可选 (参考$http config)
	ui2ng.provider("reqDataByAjax", function () {
		var unauthorizedHandler = function (response) {
			location.replace(response.headers('Location'));
		};

		this.setUnauthorizedHandler = function (handler) {
			unauthorizedHandler = handler;
		};

		this.$get = function ($http, $q, $log) {
			return function (url, params, method, config) {
				var deferred = $q.defer();

				method || (method = "GET");
				config || (config = {});
				config.url || (config.url = url);
				config.method || (config.method = method);
				config.headers || (config.headers = {});

				if (config.method == 'GET' || config.data)
					config.params || (config.params = params);
				else
					config.data = params;

				config.paramSerializer = '$httpParamSerializerJQLike';
				config.headers["X-Requested-With"] = "XMLHttpRequest";

				$http(config).then(function (response) {
					response.data.data.params = response.data.params;
					deferred.resolve(response.data.data);
				}, function (response) {
					$log.error("reqDataByAjax failed:", response.status, "(" + response.statusText + ")", config);
					deferred.reject(response);
					if (response.status === 401)
						unauthorizedHandler(response);
				});

				return deferred.promise;
			}
		};
	});

	ui2ng.factory("parseDataForChart", function () {
		return parseDataForChart;
	});

	function makeEventHandler(scope, element, attrs, eventType) {
		var attrName;
		// build attrName
		$.each(eventType.split('.').reverse(), function (i, val) {
			val = val.toLowerCase();
			if (!attrName)
				attrName = val;
			else
				attrName += val.substr(0, 1).toUpperCase() + val.substr(1);
		});

		return function () {
			var args = [eventType, element];
			args.push.apply(args, arguments);

			if (scope.$$phase) {
				if (attrs[attrName])
					scope.$eval(attrs[attrName], { params: args });
				scope.$root.$broadcast.apply(scope.$root, args);
			} else {
				scope.$apply(function () {
					if (attrs[attrName])
						scope.$eval(attrs[attrName], { params: args });
					scope.$root.$broadcast.apply(scope.$root, args);
				});
			}
		};
	}

	function processEchartsLineBarOption(option, seriesIndex, data) {
		var series = option.series[seriesIndex];
		option.legend && (option.legend.data = data.rows);

		var axisIndex = typeof (series.xAxisIndex) === 'undefined' ? 0 : series.xAxisIndex;
		var axis = option.xAxis instanceof Array ? option.xAxis[axisIndex] : option.xAxis;
		var axisType = axis.type || 'category';
		axisType === 'category' && (axis.data = data.cols);

		var axisIndex = typeof (series.yAxisIndex) === 'undefined' ? 0 : series.yAxisIndex;
		var axis = option.yAxis instanceof Array ? option.yAxis[axisIndex] : option.yAxis;
		var axisType = axis.type || 'value';
		axisType === 'category' && (axis.data = data.cols);

		series.name = data.rows[seriesIndex];
		series.data = data.data[seriesIndex];
		return option;
	}

	function processEchartsPieMapGaugeOption(option, seriesIndex, data) {
		var series = option.series[seriesIndex];
		option.legend && (option.legend.data = data.cols);

		var seriesData = [];
		for (var i = 0; i < data.data[seriesIndex].length && i < data.cols.length; ++i) {
			seriesData[i] = {
				value: data.data[seriesIndex][i],
				name: data.cols[i]
			};
		}

		series.name = data.rows[seriesIndex];
		series.data = seriesData;
		return option;
	}

	function processEchartsRadarOption(option, seriesIndex, data) {
		if (seriesIndex !== 0)
			return;

		var series = option.series[seriesIndex];
		option.legend && (option.legend.data = data.rows);

		var seriesData = [];
		for (var i = 0; i < data.data.length && i < data.rows.length; ++i) {
			seriesData[i] = {
				value: data.data[i],
				name: data.rows[i]
			};
		}

		series.data = seriesData;
		return option;
	}

	// 分析数据集
	// 参数说明:
	// rs           - 分析结果集  ，二维对象数组，必需
	// datFieldName - 数值字段名称，字符串      ，必需
	// colFieldName - 列字段名称  ，字符串      ，必需
	// rowFieldName - 行字段名称  ，字符串      ，可选
	// colsFixVal   - 固定列字段值，字符串数组  ，可选
	// rowsFixVal   - 固定行字段值，字符串数组  ，可选
	// nulVal       - 空值显示值  ，数值或字符串，默认 0
	// naVal        - 不存在显示值，数值或字符串，默认 '-'
	function parseDataForChart(rs, datFieldName, colFieldName, rowFieldName, colsFixVal, rowsFixVal, nulVal, naVal) {
		if (datFieldName instanceof Array) {
			var cols, rows = [], data = [];
			for (var i in datFieldName) {
				var fn = typeof (datFieldName[i]) === 'string' ? datFieldName[i] : datFieldName[i].fieldName;
				var dn = typeof (datFieldName[i]) === 'string' ? datFieldName[i] : datFieldName[i].displayName;

				var ret = parseDataForChart(rs, fn, colFieldName, rowFieldName,
					colsFixVal, rowsFixVal, nulVal, naVal);
				cols = ret.cols;
				for (var j in ret.rows)
					rows.push(ret.rows[j] + dn);
				Array.prototype.push.apply(data, ret.data);
			}
			return { cols: cols, rows: rows, data: data };
		}

		var colsFixed = colsFixVal && (colsFixVal instanceof Array);
		var rowsFixed = rowsFixVal && (rowsFixVal instanceof Array);

		var cols = colsFixed ? colsFixVal : [];
		var rows = rowsFixed ? rowsFixVal : [];
		var data = [];
		var ri = -1,
			rv = null,
			d = null;

		// 单系列数据
		if (!rowFieldName) {
			if (!rowsFixed) {
				rows[++ri] = '数值';
			} else {
				ri = rows.length ? 0 : -1;
			}

			d = [];
			(ri > -1) && (data[ri] = d);
		}

		for (var i = 0; i < rs.length; ++i) {
			var item = rs[i];

			// 一个系列开始了?
			if (rowFieldName && item[rowFieldName] != rv) {
				rv = item[rowFieldName];

				ri = rows.indexOf(rv);
				if (ri == -1 && !rowsFixed)
					ri = rows.push(rv) - 1;

				d = (ri > -1 && data[ri]) || [];
				(ri > -1) && (data[ri] = d);
			}

			// 查找列位置
			var ci, cv;
			if (!colFieldName) {
				if (!rowFieldName) {
					ci = i;
					(!colsFixed) && (cols[ci] = i + 1);
				} else {
					ci = 0;
					(!cols.length) && (cols[ci] = 1);
				}
			} else {
				cv = item[colFieldName];
				ci = cols.indexOf(cv);
				if (ci == -1 && !colsFixed)
					ci = cols.push(cv) - 1;
			}

			// 填入数据
			(ci > -1) && datFieldName && (d[ci] = item[datFieldName]);
		}

		// 标记不存在数据
		(nulVal === undefined) && (nulVal = 0);
		(naVal === undefined) && (naVal = '-');
		for (var i = 0; i < rows.length; ++i) {
			var r = data[i] || (data[i] = []);
			for (var j = 0; j < cols.length; ++j) {
				if (r[j] === null)
					r[j] = nulVal;
				else if (r[j] === undefined)
					r[j] = naVal;
			}
		}

		// 返回处理结果
		return {
			rows: rows,
			cols: cols,
			data: data
		};
	}

	function makeDependsLoader(js, css) {
		var loadingStatus;
		return function ($q) {
			var deferred = $q.defer();
			if (loadingStatus === true || window.LazyLoad === undefined) {
				deferred.resolve(true);
				return deferred.promise;
			} else if (loadingStatus instanceof Array) {
				loadingStatus.push(deferred);
				return deferred.promise;
			}

			loadingStatus = [deferred];
			css && LazyLoad.css(css);
			LazyLoad.js(js, function () {
				for (var i in loadingStatus)
					loadingStatus[i].resolve(true);
				loadingStatus = true;
			});

			return deferred.promise;
		};
	}

})();
